
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack')

const htmlWebpackPlugin = require("html-webpack-plugin"); //打包html到bundle资源中
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); //出口目录每次打包都更新
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //抽离css
// const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); //压缩css

// webpack默认配置文件是webpack.config.js
// 在webpack5里，如果没有额外配置的话，入口文件就是src\index.js


// Hash chunkhash contenthash 的区别
// 1.hash：整个项目得hash值，每构建一次就会有一个hash值  可以指定长度，默认是20位
// 2.chunkhash：根据不同入口entry进行依赖解析，构建对应的chunk，生成相应的hash，只要组成entry的模块没有内容改动，则对应的hash不变
// 3.contenthash：在MiniCssExtractPlugin中提取css文件，如果只是index.js改变js代码，没有改变import中css文件的代码，那么css文件名就不应该改变

module.exports = {
    // 当前编译 的环境
    mode:'development',//node production development
    // 指定项目打包的入口
    entry:'./src/index.js',
    // entry:['./src/index.js','./src/other.js'], //不是多入口， webpack会⾃动⽣成另外⼀个⼊⼝模块，并将数组中的每个指定的模块加载进来，并将最后⼀个模块的module.exports作为⼊⼝模块的module.exports导出。
    // entry:{
    //     main:'./src/index.js', //默认值 等价于entry:'./src/index.js',
    // },
    // 一旦出现了import()懒加载，那么被import的模块个它依赖的模块分割出去生产一个新的代码块
    // entry:{  //多入口必须对应多出口  1个chunk（代码块，一个chunk可以由多个module组成）对应一个bundle(打包构建生产的资源文件)
    //     index:'./src/index.js', 
    //     other:'./src/other.js', 
    // },

    // entry:{ webpack5的变化
    //     main:{
    //         import:['./src/index.js']
    //     }
    // },



    // 打包出口，
    output:{
        // 构建的资源放哪里,必须是绝对路径  默认是dist
        path:path.resolve(__dirname,'dist'),
        // 构建的文件叫什么  默认是main.js
        // filename:'index.js'
        // 多出口用占位符解决，无论多出口还是单出口都推荐使用占位符
        // filename:'[name]-[chunkhash:6].js'，
        filename:'[name]-[hash:6].js'
        // 占位符
        // 1.hash //整个项目得hash值，每构建一次就会有一个hash值  可以指定长度，默认是20位
        // 2.chunkhash  //根据不同入口entry进行依赖解析，构建对应的chunk，生成相应的hash，
                        //只要组成 entry的模块没有内容改动，则对应的hash不变
        // 3.name  //默认值是main
        // 4.id
    },

    // devtool：源代码与打包后的代码的映射关系，通过sourceMap定位到源代码。
    // js代码中首先听过babel-loader进行转译，此时会产生一个映射信息，再通过webpack编译再产生一个映射信息。
    // devtool:'eval', //编译过后代码被eval包裹
    // devtool:'source-map', //生产map文件，指向最原始的源代码，包含具体的行和列。 使用sourcemap的缓存，可以提升重复构建的速度 如果你生成的是一个单独的map文件，所有的模块的map信息耦合在一起的。 如果一个模块发生变化，整表map文件都要重新计算生成
    // devtool:'eval-source-map',每个模块的map文件单独存放，可以单独缓存。 有一个模块发生变更 ，只需要重新计算这一个模块的map信息就可以了
    // devtool:'cheap-source-map', //指向babel-loader转移过后的代码，只包含具体的行
    // devtool:'cheap-module-source-map', //指向最原始的源代码，只是把列的信息去除掉
    // devtool:'inline-source-map', //不会生成map文件
    devtool:false,

    // 监听文件变化，变化后重新自动打包
    // watch:true,//默认值是false,监听文件的变化，
    // watchOptions:{
    //     ignored:/node_modules/,//忽略文件，不监听此目录里的文件变化 
    //     aggregateTimeout:300,//防抖  
    //     poll:1000//原理是轮询，是每隔一段时间监控文件的变化，文件变化之后重新打包。 每秒问1000次
    // }, 

   
    devServer:{  // devServer其实内部就是一个express服务器
        port: 8080,//配置开发预览服务器的端口号8080
        open: true,//打包后会自动打开浏览器
        // proxy:{
        //     // 把访问路径是以/api开头的请求都转发到http://localhost:3000
        //     '/api':{
        //         target:'http://localhost:3000', //重定向的域名
        //         pathRewrite: { //重写的路径
        //             "^/api": ""
        //         }
        //     }
        // }
        //在 webpack-dev-server静态资源中间件处理之前，可以用于拦截部分请求返回特定内容，可以实现简单的mock
        onBeforeSetupMiddleware({ app }) {
            app.get('/api/users', (req, res) => {
                res.json([{ id: 1, name: "张三" }, { id: 2, name: "李四" }]);
            });
        }
        // webpack-dev-server原理可以看middleware.js文件
    },
    resolve:{ //配置模块如何解析,例如，当在 ES2015 中调用 import "lodash"，resolve 选项能够对 webpack 查找 "lodash" 的方式去做修改
        
        alias:{ // 配置路径别名
            '@':path.resolve('src')
        },
        extensions:[ //自动解析确定的扩展,能够使用户在引入模块时不带扩展.
            ".js", ".json"
        ],
        modules:[ //告诉 webpack 解析模块时应该搜索的目录。
            // 如果你想要添加一个目录到模块搜索目录，此目录优先于 node_modules/ 搜索：
            path.resolve(__dirname, "src"), "node_modules"
        ]
    },
    // 如果html上引入库的cdn，而js文件中也使用了这个库，那么打包的时候可以使用externals不将js中库打包，而是直接调用cdn
    // 如你配置了externals,key是库的名字，value是全局变量名
    // 以后再引入这个库的时候，直接从全局变量名上取值即可,不再进行打包
    externals: { //防止将某些import的包(package)打包到 bundle 中，而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
        lodash: '_'
    },
    
    // webpack会先根据resolveLoader里面的loader，没有的话再查找依赖中
    resolveLoader:{ //仅用于解析webpack的loader包，选项与上面的resolve对象的属性集合相同
        alias:{
            'babel-loader':path.resolve('loaders/babel-loader.js'),
            'file-loader':path.resolve('loaders/file-loader.js'),
        },
        modules:[  // 不需要挨个配置别名，查询顺序是先查找path.resolve('loaders')，然后再查找node_modules
            path.resolve('loaders'),'node_modules'
        ] 
    },
    module:{
        // webpack默认情况下只能处理和理解javascript和json文件
        // 如果要想引入其它类型的文件，比如css,需要对源文件进行加载和转换，转成JS
        // 比如说处理css文件 ['style-loader', 'css-loader']，从右向左执行的
        // 1.先读出源文件 index.css
        // 2.把文件内容传递给css-loader,css-loader可以处理css中的@import和url语法,,处理完之后会把内容传递给style-loader
        // 3.style-loader的作用是把CSS转换成style标签插入页面中
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            url: true,//启用/禁用url解析 url(./kf.jpg);
                            import: true, //是否允许或者说禁 用@import语法处理 @import "base.css";
                            modules: false,// 是否允许 CSS 模块化, 他会对当前的css类名或者ID重新命名，改成对应的hash值，这样的话肯定可以保证类名不重复
                            sourceMap: false,//是否生成sourcemap
                            importLoaders: 1, //允许或者说启动几个数量的loaders应用在import 的文件
                            esModule: false //默认情况下，css-loader生成使用ES Module的模块对象，如果你亩false的话，不包装成ESMODULES
                        }
                    },
                    'postcss-loader'
            ]
            },
            {
                test:/\.less$/,
                use:['style-loader','css-loader','postcss-loader','less-loader']
            },
            {
                test:/\.scss$/,
                use:['style-loader','css-loader','postcss-loader','sass-loader']
            },
            {
                // webpack4 关于图片需要使用file-loader url-loader
                // webpack5 不需要
                // file-loader=>asset/resource 把图片拷贝到输出目录里去，返回一个输出后的路径，包括文件
                // url-loader=>asset/inline 不拷贝文件，直接把源文件变成base64字符串内嵌到输出结果
                // raw-loader => asset/source 导出资源的源代码
                // asset  必定会输出一个文件
                test: /\.(jpg|png|bmp|gif)/,
                type: 'asset/resource',
                generator: {
                    filename: '[hash][ext]'
                },
                parser: {
                    //如果asset后面没加其他的字段，那么会根据这个条件做选择，如果小于maxSize的话就变成base64字符串，如果大于的就拷贝文件并返回新的地址
                    dataUrlCondition: {
                        maxSize: 4 * 1024 // 4kb
                    }
                },
            },
            {
                // 在src下的文件中使用第三方包
                // 1.直接引入：每次使用都需要手工导入(require('XXXXXXXXX'))
                // 2.插件引入：如果使用webpack.ProvidePlugin插件引入的话，则不再需要你在模块手工引入。但在没有全局变量，你在模块外是不能访问的
                // 3.如果想在任何地方访问此变量，需要把此变量设置为全局环境变量,expose-loader 可以把模块添加到全局对象上。例如添加的是isarray，就可以使用window.isarray访问到，
                // 以上三种方式，都需要打包库的代码，不能使用CDN

                // 只需要在第一个文件中引入一次这个模块即可，子文件中不再需要引入
                test: /isarray/, //对应的模块
                use: [
                    {
                        loader: 'expose-loader', //可以把模块添加到全局对象上
                        options: {
                            // 暴露出来
                            exposes: {
                                globalName: 'isarray',
                                override: true //覆盖
                            }
                        }
                    }
                ]
            },
            // js中有些代码webpack不认识，例如react，jsx。
            // 有些代码浏览器不兼容，例如es6  es7
            // 需要把他们进行编译转换
            // 如果在模块中需要支持装饰器，需要在jsconfig.json文件设置 "experimentalDecorators": true
            {
                test:/\.js$/,
                use:[{
                    loader: 'babel-loader', //babel-loader只是一个转换函数，并不能识别js语法，也不知道如何转换。
                    //@babel/core 它是babel核心模块，它认识JS代码，能够识别JS代码 不知道如何转换写法
                    options:{
                        // babel插件知道如何把老语法转成新的语法，每个插件会对应一个语法 比如说箭头函数 。  
                        // presets预设是插件的集合，把插件打包成preset预设。预设就是插件的集合 es6->es5的所有的插件打包一个包 @babel/preset-env
                        // Babel编译顺序为首先plugins从左往右,然后presets从右往左
                        presets: ["@babel/preset-env", '@babel/preset-react'],
                        plugins: [  
                            //plugin-transform-arrow-functions 可以把箭头函数转成普通函数
                            ["@babel/plugin-proposal-decorators", { legacy: true }], //支持装饰器
                            ["@babel/plugin-proposal-private-property-in-object", { "loose": true }], 
                            ["@babel/plugin-proposal-private-methods", { "loose": true }],  
                            ["@babel/plugin-proposal-class-properties", { loose: true }], //支持类的属性
                            //关于 loose  legacy 的配置说明可以看doc/1.js文件 
                            // https://babeljs.io/docs/en/babel-plugin-proposal-class-properties
                        ]
                    }
                }]
            },
            // 安装vscode的eslint插件, 可以配置自动修复参数，在代码保存的时候自动修复
            // {
            //     test: /\.js$/,
            //     loader: 'eslint-loader', // 可以进行代码的检查
                // loader是有分类 post(后置) inline(行内也被称为内联) normal(正常) pre(前置)
                // 对一个JS文件，可以有多个loader进行处理
                // 执行顺序 pre  => normal   =>   inline =>post
            //     enforce: 'pre', // loader的分类
            //     options: { fix: true }, // 如果发现有问题的可以自动修复
            //     exclude: /node_modules/, //排除
            // },
        ]
    },
    // 插件可以执行范围更广的任务
    plugins:[
        // 导出html
        new HtmlWebpackPlugin({
            template:'./src/index.html'
        }),
        new CleanWebpackPlugin(
            {cleanOnceBeforeBuildPatterns:['**/*']}
        ),
        // 如果使用webpack.ProvidePlugin插件引入的话，则不再需要你在模块手工引入
        // new webpack.ProvidePlugin({
        //     isarray:'isarray'
        // }),

        // 有些静态文件没有依赖，也需要打包，但需要上线后可以访问
        // new CopyWebpackPlugin({
        //     patterns:[
        //         {
        //         from:path.resolve(__dirname,'public'),
        //         to:path.resolve(__dirname,'dist/public')
        //         }
        //     ]
        // }),

        // 把项目中用到的css，less，sass文件都提取出来放到main.css中
        new MiniCssExtractPlugin({
            filename:'css/main.css'
        })
    ]
}

// webpack4之后引入mode概念
// none 未指定.如果设置为none,当前webpack不会启动任何额外的插件
// production 生产环境 webpack会针对构建结果进行生成环境的优化 默认值.如果设置为production，webpack会默认启动一些生产环境插件 比如压缩JS
// development 开发环境 webpack不会对代码进行压缩.如果设置为development，webpack会默认启动一些开发环境插件 比如说sourcemap
// 日常项目开发中，我们会有两套环境
// 1.一套是开发环境 用于开发时使用，构建结果用于本地的开发调试，不压缩代码，打印日志，包含sourcemap文件
// 2.一套是构建后直接上线的 代码一般都是压缩后，不打印LOG，静态文件也不包含sourcemap

// 如何动态设置不同的环境
// 1.--mode用来设置模块内的process.env.NODE_ENV.可以在模块内通过process.env.NODE_ENV获取当前的环境变量,无法在webpack配置文件中获取此变量
// 2.--env用来设置webpack配置文件的函数参数,无法在模块内通过process.env.NODE_ENV访问
// 3.cross-env用来设置node环境的process.env.NODE_ENV
// 4.DefinePlugin用来设置模块内的全局变量,可以用于模块内的字符串替换

// mode优先级
// 默认优先级(production) < 配置文件webpack.config.js里的mode < package.json中的--mode的配置
// webpack会在编译 阶段把process.env.NODE_ENV替换成对应的值

// process.env.NODE_ENV 这个指的是当前webpack打包时node环境中得变量。
// process代表当前node进程，env代表环境变量，NODE_ENV代表一个key
// cross-env NODE_ENV=production相当于设置node环境变量了，改变process.env.NODE_ENV
// 设置环境变量的方式在window和mac里不一样

/* module.exports = (env,argv)=>{
    console.log(env,argv)
    return {
        // mode: env.production?'production':'development',//node production development
        mode: process.env.NODE_ENV==='production'?'production':'development',//node production development
        指定项目打包的入口
        entry:'./src/index.js',
        output:{
            path:path.resolve(__dirname,'dist'),
            指定输出文件名
            filename:'main.js'
        },
        devtool:'source-map',
        module:{
            rules:[
                {
                    test:/\.css$/,
                    use:['style-loader','css-loader']
                }
            ]
        },
        plugins:[
            new HtmlWebpackPlugin({
                template:'./src/index.html'
            }),
            new webpack.DefinePlugin({
                'process.env.NODE_ENV':JSON.stringify(process.env.NODE_ENV)
            })
        ]
    }
} */


/* const {merge } = require('webpack-merge')
module.exports = (env) => {
    console.log('sddddddddddddd')
    // cross-env是抹平win和linux平台路径差异
    // window路径  \\\\
    // linux路径  ///
    if(process.env.NODE_ENV==='test'){
        return  merge(baseConfig,proConfig)
    }else{
        return merge(baseConfig,devConfig)
    }


    // 如果外部传去env.production   是生成
    // 如果外部传去env.XXX   是开发
    // if(env && env.production){
    //     return  merge(baseConfig,proConfig)
    // }else{
    //     return merge(baseConfig,devConfig)
    // }
} */